﻿using System;
using System.Collections.Generic;
using Xtensive.Orm;
using Xtensive.Orm.Model;
using Xtensive.Orm.Services;

namespace LINQPadDriver
{
  public abstract class GridWrapper
  {
    private static readonly Dictionary<Type, TypeInfo> typeCache = new Dictionary<Type, TypeInfo>();
    private readonly Dictionary<string, object> values = new Dictionary<string, object>();

    internal Entity Entity { get; set; }

    public void Delete()
    {
      Entity.Remove();
    }

    public void Insert()
    {
      TypeInfo type = GetGetModelType();
      Entity = (Entity) Activator.CreateInstance(type.UnderlyingType);
      var accessor = new DirectPersistentAccessor(Entity.Session);
      foreach (var pair in values) {
        FieldInfo field = Entity.TypeInfo.Fields[pair.Key];
        try {
          accessor.SetFieldValue(Entity, field, pair.Value);
        }
        catch (NotSupportedException) {
        }
      }
    }

    #region Non-public methods

    protected object GetField(string name)
    {
      object result;
      if (values.TryGetValue(name, out result))
        return result;
      FieldInfo field;
      if (Entity==null) {
        TypeInfo type = GetGetModelType();
        field = type.Fields[name];
        object defaultValue = field.DefaultValue;
        if (defaultValue==null && field.ValueType.IsValueType)
          return Activator.CreateInstance(field.ValueType);
        return defaultValue;
      }
      var accessor = new DirectPersistentAccessor(Entity.Session);
      field = Entity.TypeInfo.Fields[name];
      return accessor.GetFieldValue(Entity, field);
    }

    protected void SetField(string name, object value)
    {
      values[name] = value;
    }

    internal void Save()
    {
      var accessor = new DirectPersistentAccessor(Entity.Session);
      foreach (var pair in values) {
        FieldInfo field = Entity.TypeInfo.Fields[pair.Key];
        accessor.SetFieldValue(Entity, field, pair.Value);
      }
    }

    private TypeInfo GetGetModelType()
    {
      TypeInfo result;
      if (typeCache.TryGetValue(GetType(), out result))
        return result;
      string s = GetType().Name;
      result = Session.Demand().Domain.Model.Types[s.Substring(0, s.Length - "Wrapper".Length)];
      typeCache[GetType()] = result;
      return result;
    }

    #endregion
  }
}